#include <fstream>
#include <memory>
#include <string>
#include <unordered_map>

struct MyRecord
{
    // 这里的数据都是基本数据类型
    // 注意string需要包含cereal对应的头文件
    uint8_t x;
    std::string y;
    float z;
};

// 这里使用serialize方法进行序列化MyRecord类，该函数可实现在函数外，也可实现在函数内
// 除了serialize方法外，还有save和load方法分别用来序列化和反序列化，
// 一般来说优先使用只需实现一个的serialize方法
template <class Archive>
void serialize(Archive &ar, MyRecord &m)
{
    ar(m.x, m.y, m.z);
}

class SomeData
{
public:
    SomeData() = default;
    ~SomeData() = default;

    void gen_data()
    {
        id_ = 10;
        data_ = std::make_shared<
            std::unordered_map<uint32_t, std::shared_ptr<MyRecord>>>();
        for (int i = 0; i < id_; i++)
        {
            (*data_)[i] = std::make_shared<MyRecord>();
            (*data_)[i]->x = i;
            (*data_)[i]->y = std::to_string(i + 1);
            (*data_)[i]->z = i + 2;
        }
    }

    void show()
    {
        printf("id %d\n", id_);
        for (auto &itr : *data_)
        {
            printf("key %d, x %d, y %s, z %f\n", itr.first, itr.second->x,
                   itr.second->y.c_str(), itr.second->z);
        }
    }

private:
    // 这里使用save和load方法实现对象的序列化及反序列化
    // save和load方法可以设置为私有，但设置为私有后需要设置cereal::access类为该类的友元
    // friend class cereal::access;
    template <class Archive>
    void save(Archive &ar) const { ar(id_, data_); }

    template <class Archive>
    void load(Archive &ar) { ar(id_, data_); }

private:
    int32_t id_;
    // cereal不支持原生指针，但支持智能指针
    std::shared_ptr<std::unordered_map<uint32_t, std::shared_ptr<MyRecord>>>
        data_;
};

void test_cereal()
{
    // 注意，cereal的Archive使用了RAII，Archive在析构时才保证数据由缓存写入到文件
    // 因此，使用大括号确保archive对象在离开作用域后执行析构函数，从而将数据flush
    {
        SomeData some_data;
        some_data.gen_data();

        // 这里生成二进制数据
        // std::ofstream os("out.cereal", std::ios::binary);
        // cereal::BinaryOutputArchive archive(os);

        // 若要序列为xml，调整为XMLOutputArchive即可，json同理
        std::ofstream os("data.xml");
        // cereal::XMLOutputArchive archive(os);

        // archive(some_data);
    }

    {
        SomeData some_data;
        // 对刚刚序列化生成的out.cereal文件进行反序列化
        // std::ifstream os("out.cereal", std::ios::binary);
        // cereal::BinaryInputArchive archive(os);

        // 若要反序列为xml，调整为XMLInputArchive即可，json同理
        std::ifstream os("data.xml");
        // cereal::XMLInputArchive archive(os);

        // archive(some_data);
        // some_data.show();
    }
}

int main()
{
    test_cereal();
    return 0;
}